package com.lujianfei.ann.ui.subui;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JLabel;

import com.lujianfei.ann.base.BaseChildWindow;
import com.lujianfei.ann.bean.DataPoint;
import com.lujianfei.ann.bean.MaxMinResult;
import com.lujianfei.ann.utils.MathTool;
import com.lujianfei.ann.widget.ChartDrawPanel;
import com.lujianfei.lib.sdautolayout.impl.SDAutoLayout;

public class GradientDescentFrame extends BaseChildWindow {

	ChartDrawPanel drawPanel = new ChartDrawPanel();
	JButton btStartTraining = new JButton("开始训练");
	JButton btReset = new JButton("重置");
	MathTool mathTool = MathTool.getInstance();
	JLabel label = new JLabel();

	double w = 0;
	double b = 0;

	MaxMinResult maxMinX;
	MaxMinResult maxMinY;
	
	int iteration_index = 0;

	public static GradientDescentFrame create() {
		return new GradientDescentFrame();
	}

	private GradientDescentFrame() {
		super(800, 800);
		setTitle("梯度下降实现线性回归");
		add(drawPanel);
		add(btStartTraining);
		add(btReset);
		add(label);
		positionView();
		drawPanel.setMinX(0);
		drawPanel.setMaxX(600);
		drawPanel.setMinY(0);
		drawPanel.setMaxY(600);
		reset();
		drawPanel.repaint();

		btStartTraining.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				train();
				showLine();
				drawPanel.repaint();
			}
		});
		btReset.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				reset();
				drawPanel.repaint();
			}
		});
	}


	private void reset() {
		w = 0;
		b = 0;
		drawPanel.setPoints(getPoint());
		getMaxMin();
		showLine();
	}
	
	private void getMaxMin() {
		int[] setX = drawPanel.getSetX();
		int[] setY = drawPanel.getSetY();
		maxMinX = MathTool.getInstance().getMaxMin(setX);
		maxMinY = MathTool.getInstance().getMaxMin(setY);
	}

	@Override
	protected void onResize() {
		// TODO Auto-generated method stub
		super.onResize();
		positionView();
	}

	private void positionView() {
		SDAutoLayout.forComponent(btStartTraining).widthIs(100).heightIs(35).topSpaceToComponent(this, 5)
				.leftSpaceToComponent(this, 5);
		SDAutoLayout.forComponent(btReset).widthIs(100).heightIs(35).topSpaceToComponent(this, 5)
		.leftSpaceToComponent(btStartTraining, 5);
		
		SDAutoLayout.forComponent(label).widthIs(getWidth()-btStartTraining.getWidth()).heightIs(btStartTraining.getHeight()).topSpaceToComponent(this, 5)
		.leftSpaceToComponent(btReset, 5);
		
		
		SDAutoLayout.forComponent(drawPanel).widthIs(getWidth() - 50).heightIs(getHeight() - 100)
				.topSpaceToComponent(btStartTraining, 5);
	}

	protected void train() {
		// TODO Auto-generated method stub
		float eta = 0.1f;
		double totalError = 0;
		for (DataPoint point : drawPanel.getPoints()) {
			// get x and y value for one sample
			double xi = MathTool.getInstance().normalize(point.getX(), maxMinX);
			double yi = MathTool.getInstance().normalize(point.getY(), maxMinY);
			// 公式1
			double zi = xi * w + b;
			// 公式3
			double dz = zi - yi;
			// 公式4
			double dw = dz * xi;
			// 公式5
			double db = dz;
			// update w,b
			w = w - eta * dw;
			b = b - eta * db;
			totalError += dz * dz;
		}
		++ iteration_index;
		double d = totalError / drawPanel.getPoints().length;
		label.setText(String.format("迭代次数:%s        当前方差:%.4f        (w,b)=(%.2f,%.2f)", iteration_index,d,w,b));
	}

	private double linearEquation(double x, double w, double b) {
		return w * x + b;
	}

	private void showLine() {
		
		double normX1 = MathTool.getInstance().normalize(maxMinX.getMin(), maxMinX);
		double normX2 = MathTool.getInstance().normalize(maxMinX.getMax(), maxMinX);
		double normY1 = linearEquation(normX1, w, b);
		double normY2 = linearEquation(normX2, w, b);
		double y1 = MathTool.getInstance().denormalize(normY1, maxMinY);
		double y2 = MathTool.getInstance().denormalize(normY2, maxMinY);

		drawPanel.setLine(new Line2D.Double(maxMinX.getMin(), y1, maxMinX.getMax(), y2));
	}

	private DataPoint[] getPoint() {
		List<DataPoint> dataPoint = new ArrayList<DataPoint>();
		// 提供顶部数据集
		Set<String> set = new HashSet<>();
		Color color = new Color(0xffffff);
		for (int i = 0; i < 580; i += 2) {
			int x = i;
			int y = (int) ((float) x * 0.5f) + mathTool.getRandom(0, 20);
			String key = x + "" + y;
			if (!set.contains(key)) {
				dataPoint.add(new DataPoint(x, y, color));
				set.add(key);
			}
		}
		return dataPoint.toArray(new DataPoint[dataPoint.size()]);
	}

}
